use std::{
    ops::{Deref, DerefMut},
    sync::RwLock,
};

use lru::LruCache;
use once_cell::sync::Lazy;
use sled::{Db, IVec, Result, Tree};
pub type MemoryCache<K, V> = LruCache<K, V>;

pub static GLOBAL_CACHE: Lazy<RwLock<Option<sled::Db>>> = Lazy::new(|| RwLock::new(None));

pub struct CacheSyncWrapper(Tree, Tree);

impl Deref for CacheSyncWrapper {
    type Target = Tree;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}
impl DerefMut for CacheSyncWrapper {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl CacheSyncWrapper {
    pub fn insert_and_sync<K, V>(&self, key: K, value: V) -> Result<Option<IVec>>
    where
        K: AsRef<[u8]>,
        V: AsRef<[u8]>,
    {
        let key_vec: &[u8] = key.as_ref();
        let value_vec: &[u8] = value.as_ref();
        let res = self
            .0
            .insert(<&[u8]>::clone(&key_vec), <&[u8]>::clone(&value_vec))?;
        let mut insert_head = vec![1; value_vec.len() + 1];
        insert_head.extend_from_slice(value_vec);
        self.1.insert(key_vec, insert_head.as_slice())?;
        Ok(res)
    }
    pub fn remove_and_sync<K>(&self, key: K) -> Result<Option<IVec>>
    where
        K: AsRef<[u8]>,
    {
        let key_vec = key.as_ref();
        let res = self.0.remove(<&[u8]>::clone(&key_vec))?;
        self.0.insert(key_vec, &[0])?;
        Ok(res)
    }
}
pub enum CacheSyncType {
    Session,
    Page,
}
pub struct CacheDBWrapper(Db);

impl Deref for CacheDBWrapper {
    type Target = Db;
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl DerefMut for CacheDBWrapper {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl CacheDBWrapper {
    #[inline]
    pub fn open_session_tree(&self) -> Result<Tree> {
        self.0.open_tree("session")
    }

    #[inline]
    pub fn open_page_tree(&self) -> Result<Tree> {
        self.0.open_tree("page")
    }

    #[inline]
    pub fn open_sync_tree(&self) -> Result<Tree> {
        self.0.open_tree("sync")
    }

    #[inline]
    pub fn open_sync(&self, open_type: CacheSyncType) -> Result<CacheSyncWrapper> {
        let sync_tree = self.open_sync_tree()?;
        let main_tree = match open_type {
            CacheSyncType::Session => self.open_session_tree()?,
            CacheSyncType::Page => self.open_page_tree()?,
        };
        Ok(CacheSyncWrapper(main_tree, sync_tree))
    }
}
